using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Ltf.Diffs
{
	public enum DiffSide
	{
		Left,
		Right,
		Combined
	}

	public enum DiffSide3
	{
		SourceFrom,
		SourceTo,
		Target
	}

	[Flags]
	public enum MergeChangesToDisplay
	{
		None = 0,
		Conflicts = 1,
		Mergeable = 2,
		Same = 4
	}

	public abstract class DiffDocument
	{

		public List<Line> Lines = new List<Line>();
		protected MergeChangesToDisplay mergeChangesToDisplay;

		public DiffDocument(string fileName)
		{
			string[] strLines = File.ReadAllLines(fileName);
			for (int i = 0; i < strLines.Length; i++)
				Lines.Add(new Line(strLines[i], i));
		}

		public DiffDocument(string fileName, int changeSet, string author)
		{
			string[] strLines = File.ReadAllLines(fileName);
			for (int i = 0; i < strLines.Length; i++)
			{
				Lines.Add(new Line(strLines[i], i));
				Lines[i].SetLineProperties(LineState.None, changeSet, author);
			}
		}

		public abstract void LoadResults(Diff3Results results);


		public void RemoveCombinedLines(int visibleContextLines)
		{
			Stack<Line> unwantedLines = new Stack<Line>();
			int unwantedLinesSinceLastChange = 0;
			int wantedLinesSinceLastChange = visibleContextLines;
			bool inChangeBlock = false;
			foreach (Line line in Lines)
			{
				if (line.State == LineState.None)
					inChangeBlock = false;

				if (line.State == LineState.None && wantedLinesSinceLastChange == visibleContextLines)
				{
					unwantedLines.Push(line);
					unwantedLinesSinceLastChange++;
				}
				if (line.State == LineState.None && wantedLinesSinceLastChange < visibleContextLines)
					wantedLinesSinceLastChange++;

				if (line.State != LineState.None && !inChangeBlock)
				{
					inChangeBlock = true;
					wantedLinesSinceLastChange = 0;
					for (int i = 0; i < Math.Min(unwantedLinesSinceLastChange, visibleContextLines); i++)
						unwantedLines.Pop();

					unwantedLinesSinceLastChange = 0;
				}
			}
			while (unwantedLines.Count > 0)
				Lines.Remove(unwantedLines.Pop());


			// add divs in any broken section
			for (int i = 0; i < Lines.Count - 1; i++)
			{
				Line line1 = Lines[i];
				Line line2 = Lines[i + 1];
				if (line1.LineNumber.HasValue && line2.LineNumber.HasValue)
				{
					if (line2.LineNumber.Value > line1.LineNumber.Value + 1)
					{
						Line div = new Line(new string('-', 90), null, LineState.SectionBreak);
						Lines.Insert(i + 1, div);
						i++;
					}
				}
			}
		}

		public string GetDocumentText()
		{
			StringBuilder sb = new StringBuilder();
			foreach (Line line in Lines)
			{
				sb.Append(line.Text);
				sb.Append("\r\n");
			}
			return sb.ToString();
		}

		public string GetLineNumbersText()
		{
			StringBuilder sb = new StringBuilder();
			foreach (Line line in Lines)
			{
				sb.Append(line.LineNumber != null ? (line.LineNumber + 1).ToString().PadLeft(6) : string.Empty);
				sb.Append("\r\n");
			}
			sb.Append("\r\n");
			//sb.Append("\r\n");
			return sb.ToString();
		}

		internal string GetChangeInfo()
		{
			StringBuilder sb = new StringBuilder();
			foreach (Line line in Lines)
			{
				string ln = line.LineNumber != null ? (line.LineNumber + 1).ToString() : string.Empty;

				sb.Append(string.Format("{0} - {1}", line.ChangeSet.ToString().PadLeft(7), line.Author));
				sb.Append("\r\n");
			}
			//sb.Append("\r\n");
			//sb.Append("\r\n");
			return sb.ToString();
		}

		protected int MarkChanges(int into, LineState lineState, int lineCount, int blankLineOffset, bool offSetAdjustment)
		{
			for (int i = 0; i < lineCount; i++)
			{
				Lines[i + into + blankLineOffset + (offSetAdjustment ? -1 : 0)].SetLineState(lineState);
			}
			return lineCount;
		}

		protected int InsertMarkedPadding(int into, LineState lineState, int lineCount, ref int blankLineOffset, bool offSetAdjustment)
		{
			List<string> lines = CreateEmptyLineList(lineCount);
			return InsertChanges(into, lineState, lines, ref blankLineOffset, offSetAdjustment);
		}


		protected int InsertChanges(int into, LineState lineState, List<string> lines, ref int blankLineOffset, bool offSetAdjustment)
		{
			return InsertChanges(into, lineState, lines, ref blankLineOffset, offSetAdjustment,0, null);
		}

		protected int InsertChanges(int into, LineState lineState, List<string> lines, ref int blankLineOffset, bool offSetAdjustment, int extraOffset, List<string> removedLines)
		{
			foreach (string line in lines)
			{
				Line builtline = new Line(lineState, line);
				builtline.DeletedLines.Here = removedLines;
				Lines.Insert(into + blankLineOffset + (offSetAdjustment ? -1 : 0) + extraOffset, builtline);
				blankLineOffset++;
			}
			return lines.Count;
		}

		protected List<string> CreateEmptyLineList(int emptyLines)
		{
			List<string> lines = new List<string>();
			for (int i = 0; i < emptyLines; i++)
				lines.Add(string.Empty);
			return lines;
		}

	}

	public struct LineMap
	{
		public int From;
		public int To;

		public LineMap(int from, int to)
		{
			From = from;
			To = to;
		}
	}

	// nb sync'd with/ superset of ChangeRegionState
	public enum LineState
	{
		None = 0,

		Conflict = 10,

		ChangedFrom = 11,
		Inserted = 12,
		Deleted = 13,
		ChangedTo = 15,

		ConflictChange = 21,
		ConflictAdd = 22,
		ConflictDelete = 23,

		SameChange = 31,
		SameAdd = 32,
		SameDelete = 33,

		MergeableChange = 41,
		MergeableAdd = 42,
		MergeableDelete = 43,

		MergeTheirs = 100,
		MergeYours = 101,
		MergeNoLinesTheirs = 102,
		MergeNoLinesYours = 103,
		MergedOkay = 104,
		MergeUndone = 105,
		Resolved = 106,
		NotCheckedIn = 107,

		UserEdited = 150,

		SectionBreak = 200,

		SyncOnly = 300
	}

	public class RelatedLines
	{
		public List<string> Here = new List<string>();
		public List<string> Above = new List<string>();
		public List<string> Below = new List<string>();

		public RelatedLines()
		{ }

		public RelatedLines(List<string> here, List<string> above, List<string> below)
		{
			Here = here;
			Above = above;
			Below = below;
		}
	}

	public class ResolvedLines
	{
		public RelatedLines Theirs = new RelatedLines();
		public RelatedLines Yours = new RelatedLines();
		public RelatedLines Merged = new RelatedLines();

		public ResolvedLines()
		{ }

		public ResolvedLines(RelatedLines theirs, RelatedLines yours)
		{
			Theirs = theirs;
			Yours = yours;
		}
	}


	public class Line
	{
		public string Text;
		public RelatedLines DeletedLines = new RelatedLines();
		public ResolvedLines ResolvedLines = new ResolvedLines();


		public int? LineNumber;

		public LineState State;
		public LineState StateAbove;
		public LineState StateBelow;
		public int ChangeSet;

		public string Author;

		public Line(string text)
		{
			Text = text;
			LineNumber = null;
			State = LineState.None;
		}
		public Line(string text, int? number)
		{
			Text = text;
			LineNumber = number;
			State = LineState.None;
		}
		public Line(string text, int? number, LineState state)
		{
			Text = text;
			LineNumber = number;
			State = state;
		}

		public Line(LineState state, int changeSet, string author)
		{
			Author = author;
			ChangeSet = changeSet;
			State = state;
			Text = string.Empty;
			LineNumber = null;
		}

		public Line(LineState state, string text, int changeSet, string author)
		{
			Author = author;
			ChangeSet = changeSet;
			State = state;
			Text = text;
			LineNumber = null;
		}
		public Line(LineState state, string text, int? number, int changeSet, string author)
		{
			Author = author;
			ChangeSet = changeSet;
			State = state;
			Text = text;
			LineNumber = number;
		}

		public void SetLineProperties(LineState state, int changeSet, string author)
		{
			State = state;
			Author = author;
			ChangeSet = changeSet;
		}

		public Line(LineState state)
		{
			State = state;
			Text = string.Empty;
			LineNumber = null;
		}

		public Line(LineState state, string text)
		{
			State = state;
			Text = text;
			LineNumber = null;
		}

		public Line(LineState state, string text, List<string> resolvedTheirs, List<string> resolvedYours)
		{
			State = state;
			Text = text;
			LineNumber = null;
			ResolvedLines.Theirs.Here = resolvedTheirs;
			ResolvedLines.Yours.Here = resolvedYours;
		}

		public Line(LineState state, string text, List<string> resolvedTheirs, List<string> resolvedYours, List<string> resolvedMerged)
		{
			State = state;
			Text = text;
			LineNumber = null;
			ResolvedLines.Theirs.Here = resolvedTheirs;
			ResolvedLines.Yours.Here = resolvedYours;
			ResolvedLines.Merged.Here = resolvedMerged;
		}


		public Line(LineState state, string text, int? number)
		{
			State = state;
			Text = text;
			LineNumber = number;
		}

		public void SetLineState(LineState state)
		{
			State = state;
		}



	}
}
